package com.cdc.http;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.net.UnknownHostException;
import java.util.List;
import java.util.Map;
public class HttpConnection {

    private NetworkExecutor mExecutor;

    public HttpConnection(NetworkExecutor executor) {
        this.mExecutor = executor;
    }

    /**
     * Send the handle, send only head, parameters, such as file information.
     *
     * @param request {@link BasicRequest}.
     * @return {@link Connection}.
     */
    public Connection getConnection(BasicRequest<?> request) {


        Headers responseHeaders = new Headers();
        InputStream inputStream = null;
        Exception exception = null;

        Network network = null;
        String url = request.url();
        try {
            // MalformedURLException, IOException, ProtocolException, UnknownHostException, SocketTimeoutException
            network = createConnectionAndWriteData(request);
            System.out.println(("-------Response start-------"));
            int responseCode = network.getResponseCode();
            System.out.println("responseCode="+responseCode);
            responseHeaders = parseResponseHeaders(new URI(request.url()), responseCode, network.getResponseHeaders());
            System.out.println("responseCode=4444=="+responseCode);
            // handle body
            if (hasResponseBody(request.getRequestMethod(), responseCode)) {
                System.out.println("hasResponseBody");
                inputStream = network.getServerStream(responseCode, responseHeaders);
            }
            System.out.println(("-------Response end-------"));
        } catch (MalformedURLException e) {
            //exception = new URLError("The url is malformed: " + url + ".");
        } catch (UnknownHostException e) {
           // exception = new UnKnownHostError("Hostname can not be resolved: " + url + ".");
        } catch (SocketTimeoutException e) {
           // exception = new TimeoutError("Request time out: " + url + ".");
        } catch (Exception e) {
            exception = e;
        } finally {

        }
        System.out.println(("--------------Request finish--------------"));

        return new Connection(network, responseHeaders, inputStream, exception);
    }

    /**
     * Handle retries, and complete the handle network here.
     *
     * @param request {@link BasicRequest}.
     * @return {@link Network}.
     * @throws Exception {@link #createNetwork(BasicRequest)}.
     */
    private Network createConnectionAndWriteData(BasicRequest<?> request) throws Exception {
        Network network = null;
        Exception exception = null;
        int retryCount = request.getRetryCount() + 1;
        System.out.println("retryCount:"+retryCount);
        boolean failed = true;
        for (; failed && retryCount > 0; retryCount--) {
            try {
                network = createNetwork(request);
                exception = null;
                failed = false;
            } catch (Exception e) {
                exception = e;
            }
        }
        if (failed) {
            throw exception;
        } else if (request.getRequestMethod().allowRequestBody()) {
            writeRequestBody(request, network.getOutputStream());
        }
        return network;
    }

    /**
     * The connection is established, including the head and send the handle body.
     *
     * @param request {@link BasicRequest}.
     * @return {@link HttpURLConnection} Have been established and the server connection, and send the complete data,
     * you can directly determine the response code and read the data.
     * @throws Exception can happen when the connection is established and send data.
     */
    private Network createNetwork(BasicRequest<?> request) throws Exception {
        // Pre operation notice.
        request.onPreExecute();

        // Print url, method.
        String url = request.url();


        Headers headers = request.getHeaders();
        headers.set(Headers.HEAD_KEY_CONTENT_TYPE, request.getContentType());

        // Connection.
        List<String> values = headers.getValues(Headers.HEAD_KEY_CONNECTION);
        if (values == null || values.size() == 0)
            headers.add(Headers.HEAD_KEY_CONNECTION, Headers.HEAD_VALUE_CONNECTION_KEEP_ALIVE);

        // Content-Length.
        RequestMethod requestMethod = request.getRequestMethod();
        if (requestMethod.allowRequestBody())
            headers.set(Headers.HEAD_KEY_CONTENT_LENGTH, Long.toString(request.getContentLength()));


        return mExecutor.execute(request);
    }

    /**
     * Write handle params.
     *
     * @param request      {@link BasicRequest}.
     * @param outputStream {@link OutputStream}.
     * @throws IOException io exception.
     */
    private void writeRequestBody(BasicRequest<?> request, OutputStream outputStream) throws IOException {
        // 6. Write handle body
        OutputStream realOutputStream = IOUtils.toBufferedOutputStream(outputStream);
        request.onWriteRequestBody(realOutputStream);
        IOUtils.closeQuietly(realOutputStream);

    }



    /**
     * Parse server response headers, here will save cookies.
     *
     * @param uri             according to the requested URL generated uris.
     * @param responseCode    responseCode.
     * @param responseHeaders responseHeaders of server.
     * @return response headers of server.
     */
    private Headers parseResponseHeaders(URI uri, int responseCode, Map<String, List<String>> responseHeaders) {
        // handle cookie
        // handle headers
        Headers headers = new Headers();
        System.out.println("==空的=="+responseHeaders.isEmpty());
        System.out.println(responseHeaders.entrySet().size()+",size");
        for (Map.Entry<String, List<String>> entry : responseHeaders.entrySet()) {
            String aa=entry.getKey();
            if(aa==null){
                aa="http";
            }
            System.out.println("===========================================================");
            System.out.println("entry.getKey()="+entry.getKey());
            System.out.println("entry.getValue()="+entry.getValue());
            System.out.println("===========================================================");
            System.out.println("headers="+(headers==null));
            headers.add(aa, entry.getValue());
            System.out.println("entry.getKey()="+entry.getKey());
            System.out.println("entry.getValue()="+entry.getValue());
        }
        System.out.println("handle cookie3");
        headers.set(Headers.HEAD_KEY_RESPONSE_CODE, Integer.toString(responseCode));
        // print
        System.out.println("handle cookie4");
        for (String headKey : headers.keySet()) {
            List<String> headValues = headers.getValues(headKey);
            for (String headValue : headValues) {
                StringBuilder builder = new StringBuilder();
                if (!TextUtils.isEmpty(headKey))
                    builder.append(headKey).append(": ");
                if (!TextUtils.isEmpty(headValue))
                    builder.append(headValue);
            }
        }
        return headers;
    }

    ////////// Read response body //////////

    /**
     * This requestMethod and responseCode has responseBody ?
     *
     * @param requestMethod it's come from {@link RequestMethod}.
     * @param responseCode  responseCode from server.
     * @return true: there is data, false: no data.
     */
    public static boolean hasResponseBody(RequestMethod requestMethod, int responseCode) {
        System.out.println("requestMethod:"+requestMethod.getValue()+",responseCode="+responseCode);
        return requestMethod != RequestMethod.HEAD && hasResponseBody(responseCode);
    }

    /**
     * According to the response code to judge whether there is data.
     *
     * @param responseCode responseCode.
     * @return true: there is data, false: no data.
     */
    public static boolean hasResponseBody(int responseCode) {
        return !(100 <= responseCode && responseCode < 200) &&
                responseCode != 204 &&
                responseCode != 205 &&
                !(300 <= responseCode && responseCode < 400);
    }

}


